import os
from urllib.parse import quote_plus

import pandas as pd
import math
from sqlalchemy import create_engine
from decimal import Decimal, ROUND_HALF_UP

import streamlit as st
import re

try:
    import db_manager as db
except Exception:
    from . import db_manager as db

country_code_to_cn = {
    "US": "美国",
    "CA": "加拿大",
    "EU": "欧洲",
    "KR": "韩国",
    "JP": "日本",
    "DE": "德国",
    "AT": "奥地利",
    "BE": "比利时",
    "BG": "保加利亚",
    "CY": "塞浦路斯",
    "CZ": "捷克",
    "DK": "丹麦",
    "EE": "爱沙尼亚",
    "ES": "西班牙",
    "FI": "芬兰",
    "FR": "法国",
    "GR": "希腊",
    "HR": "克罗地亚",
    "HU": "匈牙利",
    "IE": "爱尔兰",
    "IT": "意大利",
    "LT": "立陶宛",
    "LU": "卢森堡",
    "LV": "拉脱维亚",
    "MT": "马耳他",
    "NL": "荷兰",
    "PL": "波兰",
    "PT": "葡萄牙",
    "RO": "罗马尼亚",
    "SE": "瑞典",
    "SI": "斯洛文尼亚",
    "SK": "斯洛伐克",
    "CH": "瑞士",
    "LI": "列支敦士登",
    "GB": "英国",
    "MC": "摩纳哥",
    "NO": "挪威",
    "BA": "波黑",
    "IS": "冰岛",
    "RS": "塞尔维亚",
    "FL": "列支敦士登"  # FL=LI，部分系统用 FL 表示列支敦士登

}

AUXILIARY_WEIGHT_COEFFICIENT = 2.2046226218488  # 千克转磅


def estimate_logistics_cost(erp_sku, country, zip_code, state=None):
    if country not in country_code_to_cn:
        raise ValueError(f"country 只支持: {', '.join(country_code_to_cn.keys())}")

    if zip_code is None:
        zip_code = ""
    zip_code = zip_code.strip()
    # 得到货物信息，重量以及长宽高
    goods = db.select_goods(erp_sku)

    print(goods)

    logistics_types = db.select_logistics_types(country)

    surcharge_list = []

    for logistics_type in logistics_types:
        # print(f"supplier={logistics_type['supplier']}, warehouse={logistics_type['warehouse']}, express={logistics_type['express']}")
        warehouse = logistics_type['warehouse']
        express = logistics_type['express']
        supplier = logistics_type['supplier']
        #
        # if supplier != "大卖" or warehouse != "大卖多伦多1仓" or express != "FedEx":
        #     continue

        good_type, billing_weight, good_types = get_good_type(country, express, supplier, goods)
        print(f"country = {country}, supplier = {supplier}, express = {express}, warehouse={warehouse}, "
              f"good_type={good_type}, billing_weight={billing_weight}, good_types={good_types}")
        zone = None

        item = {"supplier": supplier, "express": express, "warehouse": warehouse}

        goods_type_surcharge = 0.0  # 货型附加费
        special_surcharge = 0.0  # 特殊邮编附加费
        peak_season_surcharge = 0.0  # 旺季附加费
        peak_season_type = 0.0  # 旺季附加费类型
        peak_season_surcharge_rate = None  #  旺季附加费系数
        delivery_cost = 0.0  # 快递费
        house_cost = 0.0  # 住宅费
        bunker_surcharge_fsc = None  # 燃油附加费 FSC
        energy_surcharge_fsc = None  # 能源附加费 FSC
        clearance_fee = 0.0  # 清关费
        other_cost = 0.0  # 其它附加费
        other_surcharge_fsc = None  # FSC 附加费
        self_label_cost = 0.0  #   自提面单费
        out_bound_cost = 0.0  #  出库费

        cost = 0.0  # 总费用

        if ("CA" == country or "US" == country) and zip_code is None:
            raise ValueError("国家 为 US 时，zip_code 不能为空.")
        if "US" == country:
            zone = db.select_zone(warehouse, zip_code)
        elif "CA" == country:
            zip_code = zip_code[:3]
            zone = db.select_zone_ca(zip_code, supplier, express)

        if good_type is not None and len(good_types) > 1:
            surcharge0 = get_good_type_surcharge(country, express, supplier, zone, goods, good_types[0], warehouse)
            surcharge1 = get_good_type_surcharge(country, express, supplier, zone, goods, good_types[1], warehouse)
            surcharges = [s for s in [surcharge0, surcharge1] if s is not None]
            goods_type_surcharge = max(surcharges) if surcharges else 0.0
        else:
            goods_type_surcharge = get_good_type_surcharge(country, express, supplier, zone, goods, good_type,
                                                           warehouse)

        if "ES" != country or "FAL" != supplier or "DHL" != express:
            special_surcharge = special_zip_surcharge(country, warehouse, express, supplier, billing_weight, zip_code,
                                                      good_type)

        peak_season_surcharge, peak_season_surcharge_rate, peak_season_type = get_peak_season_surcharge(country,
                                                                                                        supplier,
                                                                                                        express,
                                                                                                        good_type)
        delivery_cost, cost = get_delivery_cost(country, supplier, express, billing_weight, zip_code, zone, warehouse)
        house_cost = get_house_cost(country, supplier, express, billing_weight, zip_code)
        bunker_surcharge_fsc = db.select_bunker_surcharge(country, express, supplier)
        energy_surcharge_fsc = db.select_energy_additional_cost(supplier, express)
        clearance_fee = get_custom_clearance_cost(country, supplier, express, zip_code)       # OK
        other_cost, other_surcharge_fsc = other_additional_cost(country, supplier, express)

        if "CA" == country and "FedEx" == express:
            other_surcharge_fsc = db.select_ca_tax(state, supplier)

        # temu部分会计算自提面单费
        if "US" == country:
            self_label_cost = db.select_self_delivery_cost_by_weight(supplier, warehouse, billing_weight)
        else:
            self_label_cost = db.select_self_delivery_cost(supplier, warehouse)

        if "CA" == country and "文鼎" == supplier and "UPS" == express:
            self_label_cost = 1.8

        out_bound_cost = get_out_bound_cost(country, supplier, express, goods)

        if (out_bound_cost is None or out_bound_cost == 0.0 or
                delivery_cost is None or delivery_cost == 0.0 or
                out_bound_cost > 999 or delivery_cost > 999):
            cost = []
        else:
            cost = calculate_total_cost(goods_type_surcharge, special_surcharge, peak_season_surcharge,
                                        peak_season_type, delivery_cost, house_cost, bunker_surcharge_fsc,
                                        energy_surcharge_fsc, clearance_fee, other_cost,
                                        other_surcharge_fsc, self_label_cost, out_bound_cost,
                                        peak_season_surcharge_rate, country,
                                        supplier, express, good_type, goods)

        item = item | cost
        print(item)
        surcharge_list.append(item)

    surcharge_list.sort(key=lambda x: x.get('cost') if x.get('cost') is not None else 0)
    return surcharge_list


def get_sorted_edges_and_girth(length_cm, width_cm, height_cm, unit='cm'):
    """
    按从大到小排序长宽高（参数单位为 cm），返回最长边、次长边、最短边和围长，返回值单位可选 cm 或 in，保留两位小数。

    参数:
        length_cm (float): 长（cm）
        width_cm (float): 宽（cm）
        height_cm (float): 高（cm）
        unit (str): 返回值单位，'cm' 或 'in'，默认 'cm'

    返回:
        dict: 包含 longestEdge, secondaryLongEdge, shortestEdge, girth，单位与 unit 一致，均保留两位小数
    """
    edges = sorted([length_cm, width_cm, height_cm], reverse=True)
    factor = 1 if unit == 'cm' else 1 / 2.54
    longest_edge = round_up(edges[0] * factor, 2)
    secondary_long_edge = round_up(edges[1] * factor, 2)
    shortest_edge = round_up(edges[2] * factor, 2)
    girth = round_up(longest_edge + secondary_long_edge + shortest_edge, 2)
    return {
        "longestEdge": longest_edge,
        "secondaryLongEdge": secondary_long_edge,
        "shortestEdge": shortest_edge,
        "girth": girth
    }


def get_good_type(country, express, supplier, goods):
    """计算  货型和计费重量"""
    good_type = None
    billing_weight = 0.0
    good_types = []

    model_in = get_sorted_edges_and_girth(goods['item_pl'], goods['item_pw'], goods['item_ph'], unit='in')
    model_cm = get_sorted_edges_and_girth(goods['item_pl'], goods['item_pw'], goods['item_ph'], unit='cm')

    if "FedEx" in express or "UPS" in express or "Amazon Shipping" in express:
        good_type, good_types, billing_weight = fedex_good_type(country, express, supplier, model_in, goods)

    if "西邮" == supplier:
        if "GLS" == express:
            billing_weight, good_type = wp_gls_good_type(goods, country, model_cm)
        if "DHL" == express:
            billing_weight, good_type = wp_dhl_good_type(goods, country, model_cm)
        if "GEL" == express:
            billing_weight, good_type = wp_gel_good_type(goods, model_cm)
        if "DPD" == express:
            billing_weight, good_type = fal_dpd_good_type(goods, model_cm)
    if "FAL" == supplier:
        if "DHL" == express:
            billing_weight, good_type = fal_dhl_good_type(goods, model_cm)
        if "DPD" == express:
            billing_weight, good_type = fal_dpd_good_type(goods, model_cm)
        if "DPD特殊渠道" == express:
            billing_weight, good_type = fal_dpd_special_good_type(goods, model_cm)
        if "GLS" == express:
            billing_weight, good_type = wp_gls_good_type(goods, country, model_cm)
    if "Rush" == supplier:
        if "DHL" == express:
            billing_weight, good_type = dhl_good_type(goods, country, model_cm)
        if "DPD" == express:
            billing_weight, good_type = rush_dpd_good_type(goods, model_cm)
        if "GLS" == express:
            billing_weight, good_type = rush_gls_good_type(goods, model_cm)
    if "品晟" == supplier and "DPD" == express:
        billing_weight, good_type = pw_dpd_good_type(goods, model_cm)

    if "中链" == supplier:
        if "DPD" == express:
            billing_weight, good_type = sgs_dpd_good_type(goods, model_cm)
        if "DHL" == express:
            billing_weight, good_type = sgs_dhl_good_type(goods, model_cm)

    if "金仓" == supplier:
        if "DPD" == express:
            billing_weight, good_type = gh_dpd_good_type(goods, model_cm)
        if "DHL" == express:
            billing_weight, good_type = gh_dhl_good_type(goods, country, model_cm)
        if "GLS" == express:
            billing_weight, good_type = gh_gls_good_type(goods, model_cm)
    return good_type, round_up(billing_weight, 2), good_types


def fedex_good_type(country, express, supplier, model_in, goods):
    good_types = []
    good_type = None
    volume_weight_in = round_up((model_in['longestEdge'] * model_in['secondaryLongEdge'] * model_in['shortestEdge']) / 250)
    if "文鼎" == supplier and "FedEx" in express:
        volume_weight_in = round_up((model_in['longestEdge'] * model_in['secondaryLongEdge'] * model_in['shortestEdge']) / 305)
    auxiliary_billing_weight_tem = round_up(goods['item_gross_weight'] * AUXILIARY_WEIGHT_COEFFICIENT)
    auxiliary_billing_weight = auxiliary_billing_weight_tem if auxiliary_billing_weight_tem > volume_weight_in else volume_weight_in
    if auxiliary_billing_weight > 150 or model_in['longestEdge'] > 108 or model_in['girth'] > 165:
        good_type = "UO"
    elif model_in['girth'] > 130 or model_in['longestEdge'] > 96:
        good_type = "OS"
    else:
        if "UPS" in express:
            if "西邮" == supplier:
                if auxiliary_billing_weight_tem >= 49:
                    good_type = "AHW"
                    good_types.append("AHW")
                if model_in['longestEdge'] >= 47 or model_in['secondaryLongEdge'] >= 30 or model_in['girth'] >= 104:
                    good_type = "AHS"
                    good_types.append("AHS")
            elif "DEWELL" == supplier or "CA" == country:
                if "FexEx" in express or "CA" == country:
                    if auxiliary_billing_weight_tem >= 50:
                        good_type = "AHW"
                    elif model_in['longestEdge'] >= 48 or model_in['secondaryLongEdge'] >= 30 or model_in[
                        'girth'] >= 105:
                        good_type = "AHS"
                else:
                    if auxiliary_billing_weight_tem >= 49:
                        good_type = "AHW"
                    elif model_in['longestEdge'] >= 47 or model_in['secondaryLongEdge'] >= 30 or model_in[
                        'girth'] >= 104:
                        good_type = "AHS"
            else:
                if model_in['longestEdge'] >= 48 or model_in['secondaryLongEdge'] >= 30 or model_in['girth'] >= 105:
                    good_type = "AHS"
                elif auxiliary_billing_weight_tem >= 50:
                    good_type = "AHW"
        else:
            if "西邮" == supplier:
                if auxiliary_billing_weight_tem > 50:
                    good_type = "AHW"
                    good_types.append("AHW")
                elif model_in['longestEdge'] > 48 or model_in['secondaryLongEdge'] > 30 or model_in['girth'] > 105:
                    good_type = "AHS"
                    good_types.append("AHS")
            elif "DEWELL" == supplier:
                if "FedEx" in express:
                    if auxiliary_billing_weight_tem > 50:
                        good_type = "AHW"
                    elif model_in['longestEdge'] > 48 or model_in['secondaryLongEdge'] > 30 or model_in[
                        'girth'] > 105:
                        good_type = "AHS"
                elif "Amazon Shipping" in express:
                    if auxiliary_billing_weight_tem > 50:
                        good_type = "AHW"
                    elif model_in['longestEdge'] > 48 or model_in['secondaryLongEdge'] > 30 or model_in[
                        'girth'] > 105:
                        good_type = "AHS"
            else:
                if model_in['longestEdge'] > 48 or model_in['secondaryLongEdge'] > 30 or model_in['girth'] > 105:
                    good_type = "AHS"
                if auxiliary_billing_weight_tem > 55:
                    good_type = "AHW"

    if good_type is None:
        good_type = "Std"

    if "OS" == good_type and auxiliary_billing_weight_tem < 90:
        billing_weight = 90
    elif "AHS" == good_type and auxiliary_billing_weight_tem < 40 and "Amazon Shipping" not in express:
        billing_weight = 40
    else:
        billing_weight = auxiliary_billing_weight

    return good_type, good_types, billing_weight


def wp_gls_good_type(goods, country, model_cm):
    volume_weight_cm = (model_cm['longestEdge'] * model_cm['secondaryLongEdge'] * model_cm['shortestEdge']) / 6000
    good_type = None
    billing_weight = 0.0
    if "DE" == country:
        billing_weight = goods['item_gross_weight']

        if billing_weight > 40:
            good_type = "NA"
        elif model_cm['longestEdge'] > 200 or model_cm['secondaryLongEdge'] > 80 or model_cm[
            'shortestEdge'] > 60 or model_cm['girth'] > 300:
            good_type = "Super OS"
        elif model_cm['longestEdge'] > 150 and model_cm['longestEdge'] > 200:
            good_type = "Large OS"
        elif (model_cm['longestEdge'] * model_cm['secondaryLongEdge'] * model_cm['shortestEdge'] / 1000 > 150
              or (model_cm['longestEdge'] > 120 and model_cm['longestEdge'] < 150)):
            good_type = "OS"
        elif model_cm['shortestEdge'] < 3:
            good_type = "US"
        else:
            good_type = "Std"
    else:
        billing_weight = volume_weight_cm if volume_weight_cm > goods['item_gross_weight'] else goods[
            'item_gross_weight']
        if goods['item_gross_weight'] > 40:
            good_type = "NA"
        elif model_cm['longestEdge'] > 200 or model_cm['secondaryLongEdge'] > 80 or model_cm[
            'shortestEdge'] > 60 or model_cm['girth'] > 300:
            good_type = "Super OS"
        elif model_cm['longestEdge'] > 150 and model_cm['longestEdge'] > 200:
            good_type = "Large OS"
        elif (model_cm['longestEdge'] * model_cm['secondaryLongEdge'] * model_cm['shortestEdge'] / 1000 > 150
              or (model_cm['longestEdge'] > 120 and model_cm['longestEdge'] < 150)):
            good_type = "OS"
        elif model_cm['shortestEdge'] < 3:
            good_type = "US"
        else:
            good_type = "Std"
    return billing_weight, good_type


def wp_dhl_good_type(goods, country, model_cm):
    billing_weight = goods['item_gross_weight']
    good_type = None
    if "DE" == country:
        if (model_cm['longestEdge'] > 200 or model_cm['girth'] > 360 or goods['item_gross_weight'] > 31.5
            or model_cm['longestEdge'] < 15) or model_cm['secondaryLongEdge'] < 11 or model_cm['shortestEdge'] < 1:
            good_type = "NA"
        elif model_cm['longestEdge'] > 120 or model_cm['secondaryLongEdge'] > 60 or model_cm['shortestEdge'] > 60:
            good_type = "OS"
        elif model_cm['longestEdge'] <= 120 and model_cm['secondaryLongEdge'] <= 60 and model_cm['shortestEdge'] <= 60:
            good_type = "Std"
    else:
        if (model_cm['longestEdge'] > 150 or model_cm['girth'] > 300 or goods['item_gross_weight'] > 30
            or model_cm['longestEdge'] < 15) or model_cm['secondaryLongEdge'] < 11 or model_cm['shortestEdge'] < 1:
            good_type = "NA"
        elif model_cm['longestEdge'] > 120 or model_cm['secondaryLongEdge'] > 60 or model_cm['shortestEdge'] > 60:
            good_type = "OS"
        elif model_cm['longestEdge'] <= 120 and model_cm['secondaryLongEdge'] <= 60 and model_cm['shortestEdge'] <= 60:
            good_type = "Std"
    return billing_weight, good_type


def wp_gel_good_type(goods, model_cm):
    volume_weight = round_up(model_cm['longestEdge'] * model_cm['secondaryLongEdge'] * model_cm['shortestEdge'] * 0.00014,
                          2)
    billing_weight = goods['item_gross_weight'] if goods['item_gross_weight'] > volume_weight else volume_weight
    good_type = None
    if (model_cm['longestEdge'] > 610 or model_cm['secondaryLongEdge'] > 225
            or model_cm['shortestEdge'] > 120 or goods['item_gross_weight'] > 100):
        good_type = "NA"
    elif (320 < model_cm['longestEdge'] <= 610
          and model_cm['secondaryLongEdge'] <= 225 and model_cm['shortestEdge'] <= 120
          and 60 <= goods['item_gross_weight'] <= 100):
        good_type = "OS"
    elif (model_cm['longestEdge'] <= 320 and model_cm['secondaryLongEdge'] <= 225
          and model_cm['shortestEdge'] <= 120 and goods['item_gross_weight'] <= 60):
        good_type = "Std"
    return billing_weight, good_type


def fal_dhl_good_type(goods, model_cm):
    billing_weight = goods['item_gross_weight']
    good_type = None
    if (model_cm['longestEdge'] > 200 or model_cm['girth'] > 300
            or goods['item_gross_weight'] > 31.5 or model_cm['longestEdge'] < 15
            or model_cm['secondaryLongEdge'] < 11 or model_cm['shortestEdge'] < 1):
        good_type = "NA"
    elif model_cm['longestEdge'] > 120 or model_cm['secondaryLongEdge'] > 60 or model_cm['shortestEdge'] > 60:
        good_type = "OS"
    elif (model_cm['longestEdge'] <= 120 and model_cm['secondaryLongEdge'] <= 60
          or model_cm['shortestEdge'] <= 60):
        good_type = "Std"
    return billing_weight, good_type


def fal_dpd_good_type(goods, model_cm):
    billing_weight = goods['item_gross_weight']
    good_type = None
    if goods['item_gross_weight'] > 40 and model_cm['longestEdge'] > 250 and model_cm['girth'] > 330:
        good_type = "NA"
    elif goods['item_gross_weight'] > 31.5 or (model_cm['longestEdge'] > 175 or model_cm['girth'] > 300):
        good_type = "Large OS"
    elif ((model_cm['longestEdge'] > 120 or model_cm['secondaryLongEdge'] > 60 or model_cm['shortestEdge'] > 60
           or model_cm['longestEdge'] * model_cm['secondaryLongEdge'] * model_cm['shortestEdge'] > 150000)
          and goods['item_gross_weight'] <= 31.5 and model_cm['girth'] < 300):
        good_type = "OS"
    elif (model_cm['longestEdge'] <= 120 and model_cm['secondaryLongEdge'] <= 60
          and model_cm['shortestEdge'] <= 60 and goods['item_gross_weight'] <= 31.5 and model_cm['girth'] < 300
          and model_cm['longestEdge'] * model_cm['secondaryLongEdge'] * model_cm['shortestEdge'] <= 150000):
        good_type = "Std"
    return billing_weight, good_type


def fal_dpd_special_good_type(goods, model_cm):
    billing_weight = goods['item_gross_weight']
    good_type = None
    if goods['item_gross_weight'] > 40 or model_cm['longestEdge'] > 250 or model_cm['girth'] > 330:
        good_type = "NA"
    elif 31.5 < goods['item_gross_weight'] <= 40 and (model_cm['longestEdge'] > 175 or model_cm['girth'] > 300):
        good_type = "Large&Super OS"
    elif ((model_cm['longestEdge'] > 175 or model_cm['girth'] > 300)
          and (goods['item_gross_weight'] <= 31.5 or goods['item_gross_weight'] > 40)):
        good_type = "Large OS"
    elif 31.5 < goods['item_gross_weight'] <= 40 and model_cm['longestEdge'] <= 175 and model_cm['girth'] <= 300:
        good_type = "Super OS"
    elif (goods['item_gross_weight'] <= 31.5 and model_cm['girth'] <= 300
          and (model_cm['longestEdge'] * model_cm['secondaryLongEdge'] * model_cm['shortestEdge'] > 150000
               or model_cm['longestEdge'] > 120 or model_cm['secondaryLongEdge'] > 60 or model_cm[
                   'shortestEdge'] > 60)):
        good_type = "OS"
    elif (goods['item_gross_weight'] <= 31.5 and model_cm['girth'] <= 300
          and model_cm['longestEdge'] * model_cm['secondaryLongEdge'] * model_cm['shortestEdge'] <= 150000
          and model_cm['longestEdge'] <= 120 and model_cm['secondaryLongEdge'] <= 60 and model_cm[
              'shortestEdge'] <= 60):
        good_type = "Std"
    return billing_weight, good_type


def dhl_good_type(goods, country, model_cm):
    billing_weight = goods['item_gross_weight']
    good_type = None
    if "DE" == country:
        if (model_cm['longestEdge'] > 200 or model_cm['girth'] > 360 or goods['item_gross_weight'] > 30
            or model_cm['longestEdge'] < 15) or model_cm['secondaryLongEdge'] < 11 or model_cm['shortestEdge'] < 1:
            good_type = "NA"
        elif model_cm['longestEdge'] > 120 or model_cm['secondaryLongEdge'] > 60 or model_cm['shortestEdge'] > 60:
            good_type = "OS"
        elif model_cm['longestEdge'] <= 120 and model_cm['secondaryLongEdge'] <= 60 and model_cm['shortestEdge'] <= 60:
            good_type = "Std"
    elif "AT" == country:
        if (model_cm['longestEdge'] > 150 or model_cm['girth'] > 360 or goods['item_gross_weight'] > 30
            or model_cm['longestEdge'] < 15) or model_cm['secondaryLongEdge'] < 11 or model_cm['shortestEdge'] < 1:
            good_type = "NA"
        elif model_cm['longestEdge'] > 120 or model_cm['secondaryLongEdge'] > 60 or model_cm['shortestEdge'] > 60:
            good_type = "OS"
        elif model_cm['longestEdge'] <= 120 and model_cm['secondaryLongEdge'] <= 60 and model_cm['shortestEdge'] <= 60:
            good_type = "Std"
    else:
        if (model_cm['longestEdge'] > 150 or model_cm['girth'] > 300 or goods['item_gross_weight'] > 30
            or model_cm['longestEdge'] < 15) or model_cm['secondaryLongEdge'] < 11 or model_cm['shortestEdge'] < 1:
            good_type = "NA"
        elif model_cm['longestEdge'] > 120 or model_cm['secondaryLongEdge'] > 60 or model_cm['shortestEdge'] > 60:
            good_type = "OS"
        elif model_cm['longestEdge'] <= 120 and model_cm['secondaryLongEdge'] <= 60 and model_cm['shortestEdge'] <= 60:
            good_type = "Std"

    return billing_weight, good_type


def rush_dpd_good_type(goods, model_cm):
    billing_weight = goods['item_gross_weight']
    good_type = None
    if goods['item_gross_weight'] >= 50 and model_cm['longestEdge'] >= 280 and model_cm['girth'] > 360:
        good_type = "NA"
    elif 40 < goods['item_gross_weight'] <= 50 or 250 < model_cm['longestEdge'] < 280 or 330 < model_cm['girth'] < 360:
        good_type = "Super OS"
    elif 31.5 < goods['item_gross_weight'] < 40 or 175 < model_cm['longestEdge'] <= 250 or 300 < model_cm[
        'girth'] <= 330:
        good_type = "Large OS"
    elif (goods['item_gross_weight'] <= 31.5 or (120 < model_cm['longestEdge'] <= 175) or model_cm['girth'] <= 300
          or model_cm['longestEdge'] * model_cm['secondaryLongEdge'] * model_cm['shortestEdge'] > 150000):
        good_type = "OS"
    elif (model_cm['longestEdge'] <= 120 and model_cm['secondaryLongEdge'] <= 60
          and model_cm['shortestEdge'] <= 60 and goods['item_gross_weight'] <= 31.5 and model_cm['girth'] <= 300
          and model_cm['longestEdge'] * model_cm['secondaryLongEdge'] * model_cm['shortestEdge'] <= 150000):
        good_type = "Std"
    return billing_weight, good_type


def rush_gls_good_type(goods, model_cm):
    volume_weight = (model_cm['longestEdge'] * model_cm['secondaryLongEdge'] * model_cm['shortestEdge']) / 6000
    good_type = None
    billing_weight = goods['item_gross_weight'] if goods['item_gross_weight'] > volume_weight else volume_weight
    if goods['item_gross_weight'] >= 40 or model_cm['longestEdge'] > 200 or model_cm['girth'] > 300:
        good_type = "NA"
    elif (120 < model_cm['longestEdge'] < 200
          or model_cm['longestEdge'] * model_cm['secondaryLongEdge'] * model_cm['shortestEdge'] > 150000):
        good_type = "OS"
    else:
        good_type = "Std"
    return billing_weight, good_type


def pw_dpd_good_type(goods, model_cm):
    billing_weight = goods['item_gross_weight']
    good_type = None

    if (goods['item_gross_weight'] >= 50 and model_cm['longestEdge'] >= 280 and model_cm['girth'] >= 360):
        good_type = "NA"
    elif ((40 < goods['item_gross_weight'] < 50)
          or (250 < model_cm['longestEdge'] < 280)
          or (330 < model_cm['girth'] < 360)):
        good_type = "Super OS"
    elif ((31.5 < goods['item_gross_weight'] <= 40)
          or (175 < model_cm['longestEdge'] <= 250)
          or (300 < model_cm['girth'] <= 330)):
        good_type = "Large OS"
    elif (((120 < model_cm['longestEdge'] <= 175)
           or model_cm['secondaryLongEdge'] > 60
           or model_cm['shortestEdge'] > 60
           or model_cm['longestEdge'] * model_cm['secondaryLongEdge'] * model_cm['shortestEdge'] > 150000)
          and goods['item_gross_weight'] <= 31.5 and model_cm['girth'] <= 300):
        good_type = "OS"
    elif (model_cm['longestEdge'] <= 120 and model_cm['secondaryLongEdge'] <= 60 and model_cm['shortestEdge'] <= 60
          and goods['item_gross_weight'] <= 31.5 and model_cm['girth'] <= 300
          and model_cm['longestEdge'] * model_cm['secondaryLongEdge'] * model_cm['shortestEdge'] <= 150000):
        good_type = "Std"

    return billing_weight, good_type


def sgs_dpd_good_type(goods, model_cm):
    billing_weight = goods['item_gross_weight']
    good_type = None

    if (goods['item_gross_weight'] > 40 and model_cm['longestEdge'] > 250 and model_cm['girth'] > 330):
        good_type = "NA"
    elif (goods['item_gross_weight'] > 31.5 or model_cm['longestEdge'] > 175 or model_cm['girth'] > 300):
        good_type = "Large OS"
    elif ((model_cm['longestEdge'] > 120 or model_cm['secondaryLongEdge'] > 60
           or model_cm['shortestEdge'] > 60
           or model_cm['longestEdge'] * model_cm['secondaryLongEdge'] * model_cm['shortestEdge'] > 150000)
          and goods['item_gross_weight'] <= 31.5 and model_cm['girth'] <= 300):
        good_type = "OS"
    elif (model_cm['longestEdge'] <= 120 and model_cm['secondaryLongEdge'] <= 60 and model_cm['shortestEdge'] <= 60
          and goods['item_gross_weight'] <= 31.5 and model_cm['girth'] <= 300
          and model_cm['longestEdge'] * model_cm['secondaryLongEdge'] * model_cm['shortestEdge'] <= 150000):
        good_type = "Std"

    return billing_weight, good_type


def sgs_dhl_good_type(goods, model_cm):
    billing_weight = goods['item_gross_weight']
    good_type = None

    if (goods['item_gross_weight'] >= 50 and model_cm['longestEdge'] >= 280 and model_cm['girth'] >= 360):
        good_type = "NA"
    elif ((40 < goods['item_gross_weight'] < 50)
          or (250 < model_cm['longestEdge'] < 280)
          or (330 < model_cm['girth'] < 360)):
        good_type = "Super OS"
    elif ((31.5 < goods['item_gross_weight'] <= 40)
          or (175 < model_cm['longestEdge'] <= 250)
          or (300 < model_cm['girth'] <= 330)):
        good_type = "Large OS"
    elif (((120 < model_cm['longestEdge'] <= 175)
           or model_cm['secondaryLongEdge'] > 60
           or model_cm['shortestEdge'] > 60
           or model_cm['longestEdge'] * model_cm['secondaryLongEdge'] * model_cm['shortestEdge'] > 150000)
          and goods['item_gross_weight'] <= 31.5 and model_cm['girth'] <= 300):
        good_type = "OS"
    elif (model_cm['longestEdge'] <= 120 and model_cm['secondaryLongEdge'] <= 60 and model_cm['shortestEdge'] <= 60
          and goods['item_gross_weight'] <= 31.5 and model_cm['girth'] <= 300
          and model_cm['longestEdge'] * model_cm['secondaryLongEdge'] * model_cm['shortestEdge'] <= 150000):
        good_type = "Std"

    return billing_weight, good_type


def gh_dpd_good_type(goods, model_cm):
    billing_weight = goods['item_gross_weight']
    good_type = None

    if (billing_weight >= 40 or model_cm['longestEdge'] >= 250 or model_cm['girth'] >= 330):
        good_type = "NA"
    elif ((model_cm['longestEdge'] >= 175 or model_cm['girth'] >= 300)
          and (31 <= billing_weight < 40)):
        good_type = "Large&Super OS"
    elif ((31 <= billing_weight < 40)
          and ((model_cm['longestEdge'] * model_cm['secondaryLongEdge'] * model_cm['shortestEdge'] >= 150000)
               or model_cm['longestEdge'] >= 120 or model_cm['secondaryLongEdge'] >= 60
               or model_cm['shortestEdge'] >= 60) and model_cm['girth'] < 300):
        good_type = "OS&Super OS"
    elif (not (model_cm['longestEdge'] >= 175 or model_cm['girth'] >= 300)
          and (31 <= billing_weight < 40)):
        good_type = "Super OS"
    elif ((model_cm['longestEdge'] >= 175 or model_cm['girth'] >= 300)
          and not (31 <= billing_weight < 40)):
        good_type = "Large OS"
    elif (billing_weight <= 31 and model_cm['girth'] < 300
          and (model_cm['longestEdge'] * model_cm['secondaryLongEdge'] * model_cm['shortestEdge'] >= 150000
               or model_cm['longestEdge'] >= 120 or model_cm['secondaryLongEdge'] >= 60
               or model_cm['shortestEdge'] >= 60)):
        good_type = "OS"
    elif (billing_weight <= 31 and model_cm['girth'] < 300
          and model_cm['longestEdge'] * model_cm['secondaryLongEdge'] * model_cm['shortestEdge'] < 150000
          and model_cm['longestEdge'] < 120 and model_cm['secondaryLongEdge'] < 60 and model_cm['shortestEdge'] < 60):
        good_type = "Std"

    return billing_weight, good_type


def gh_dhl_good_type(goods, country, model_cm):
    billing_weight = goods['item_gross_weight']
    good_type = None

    if country == "DE":
        if (billing_weight >= 31 or model_cm['longestEdge'] >= 200 or model_cm['girth'] >= 360
                or model_cm['longestEdge'] <= 15 or model_cm['secondaryLongEdge'] <= 11 or model_cm[
                    'shortestEdge'] <= 1):
            good_type = "NA"
        elif model_cm['longestEdge'] >= 120 or model_cm['secondaryLongEdge'] >= 60 or model_cm['shortestEdge'] >= 60:
            good_type = "OS"
        elif model_cm['longestEdge'] < 120 and model_cm['secondaryLongEdge'] < 60 and model_cm['shortestEdge'] < 60:
            good_type = "Std"
    else:
        if (billing_weight >= 31 or model_cm['longestEdge'] >= 150 or model_cm['girth'] >= 300
                or model_cm['longestEdge'] <= 15 or model_cm['secondaryLongEdge'] <= 11 or model_cm[
                    'shortestEdge'] <= 1):
            good_type = "NA"
        elif model_cm['longestEdge'] >= 120 or model_cm['secondaryLongEdge'] >= 60 or model_cm['shortestEdge'] >= 60:
            good_type = "OS"
        elif model_cm['longestEdge'] < 120 and model_cm['secondaryLongEdge'] < 60 and model_cm['shortestEdge'] < 60:
            good_type = "Std"

    return billing_weight, good_type


def gh_gls_good_type(goods, model_cm):
    # 体积重，向上取整
    volume_weight = math.ceil(
        model_cm['longestEdge'] * model_cm['secondaryLongEdge'] * model_cm['shortestEdge'] / 6000
    )
    # 计费重，向上取整
    billing_weight = math.ceil(max(goods['item_gross_weight'], volume_weight))

    good_type = None
    if (billing_weight > 40 or model_cm['longestEdge'] > 200 or model_cm['secondaryLongEdge'] > 80
            or model_cm['shortestEdge'] > 60 or model_cm['girth'] > 300):
        good_type = "NA"
    elif (model_cm['longestEdge'] > 120
          or model_cm['longestEdge'] * model_cm['secondaryLongEdge'] * model_cm['shortestEdge'] > 150000
          or model_cm['longestEdge'] < 15 or model_cm['secondaryLongEdge'] < 11 or model_cm['shortestEdge'] < 3):
        good_type = "OS"
    else:
        good_type = "Std"

    return billing_weight, good_type


def get_good_type_surcharge(country, express, supplier, zone, goods, good_type, warehouse):
    """货型附加费"""
    if express == 'CTT':
        return 0.0

    if country == "US":
        surcharges = db.select_us_cost_type_good_surcharge(supplier, express, zone, good_type)
    elif country == "CA":
        surcharges = db.select_ca_cost_type_good_surcharge(supplier, express, warehouse, good_type)
    else:
        surcharges = db.select_eu_cost_type_good_surcharge(supplier, express, good_type)

    if not surcharges:
        return 0.0
    if len(surcharges) == 1:
        return surcharges[0]['expenses']
    return next((s['expenses'] for s in surcharges if matches_criteria(country, goods, warehouse, s)), 0.0)


def matches_criteria(country, goods, warehouse, s):
    if "US" == country or "CA" == country:
        return warehouse == s['warehouse'] and (s['str_billed_weight'] is None
                                                or (s['str_billed_weight'] <= goods['item_gross_weight'] <= s['end_billed_weight']))
    else:
        return "DE" == country or "EU" == country


def special_zip_surcharge(country, warehouse, express, supplier, billing_weight, zip_code, good_type):
    """特殊邮编附加费"""
    special_surcharge = 0.0

    is_number = bool(re.match(r"^\d+$", zip_code))

    if "US" != country and "CA" != country:
        select_supplier = supplier
    else:
        select_supplier = None

    state = None  #  这边州数据从哪里来？
    if "CA" == country:
        select_zip = zip_code.replace(' ', '')
        if "FedEx" in express:
            select_zip = zip_code[:3] if len(zip_code) >= 3 else zip_code
    elif is_number:
        select_zip = int(zip_code.replace(' ', ''))
    else:
        select_zip = zip_code.replace(' ', '')

    zip_type = db.select_special_zip_surcharge_type(country, express, state, select_supplier, select_zip)

    if country == "CA" and zip_type is not None and zip_type != '':
        return db.select_ca_cost_type_surcharge(supplier, express, warehouse, zip_type)
    elif country == "US":
        zone = db.select_zone(warehouse, zip_code)
        special_zip_surcharges = db.select_us_cost_type_surcharge(supplier, express, zip_type)
        if special_zip_surcharges:
            if len(special_zip_surcharges) != 1:
                for surcharge_item in special_zip_surcharges:
                    if warehouse == surcharge_item['warehouse']:
                        if surcharge_item.get('str_billed_weight') is not None:
                            if surcharge_item['str_billed_weight'] < billing_weight < surcharge_item['end_billed_weight']:
                                special_surcharge = surcharge_item['expenses']
                        else:
                            special_surcharge = surcharge_item['expenses']
            else:
                special_surcharge = special_zip_surcharges[0]['expenses']
    else:
        special_zip_surcharges_eu = db.select_eu_cost_type_surcharge(supplier, express, zip_type)
        if special_zip_surcharges_eu:
            if len(special_zip_surcharges_eu) != 1:
                country_name = db.select_eu_country_name(country)
                for surcharge_item in special_zip_surcharges_eu:
                    if country_name is not None and country_name != '':
                        if "DE" == surcharge_item['country']:
                            special_surcharge = surcharge_item['expenses']
                    else:
                        if "EU" == country:
                            special_surcharge = surcharge_item['expenses']
            else:
                special_surcharge = special_zip_surcharges_eu[0]['expenses']
    return special_surcharge


def get_peak_season_surcharge(country, supplier, express, good_type):
    """旺季附加费"""
    logistics_cost_peak_seasons = db.select_peck_season_surcharge(supplier, express, good_type)

    if logistics_cost_peak_seasons is None:
        return 0.0, 0.0, ''

    cost = 0.0
    rate = 0.0
    cost_type = []
    for peak_season_cost in logistics_cost_peak_seasons:
        if good_type == peak_season_cost['good_type'] and country in ['US', 'CA']:
            cost += peak_season_cost['expenses']
            cost_type.append(peak_season_cost['cost_type'])
        elif (good_type is None or good_type == '') and peak_season_cost['expenses'] is not None:
            cost += peak_season_cost['expenses']
            cost_type.append(peak_season_cost['cost_type'])
        elif peak_season_cost['rate'] is not None:
            cost_type.append(peak_season_cost['cost_type'])
            rate = cost + peak_season_cost['rate']

    if cost != 0.0:
        return cost, None, '+'.join(cost_type)
    elif rate != 0.0:
        return 0.0, rate, '+'.join(cost_type)
    return 0.0, None, ''

def get_delivery_cost(country, supplier, express, billing_weight, zip_code, zone, warehouse):
    """获得快递费"""
    discount = db.select_discount(supplier, express)     #  根据数据库数据这边永远返回0.0
    region = None
    islands = None

    delivery_cost = 0.0
    cost = 0.0

    if "CTT" == express:
        zone = db.select_ctt_zone1(zip_code)
        if zone:
            region = zone['zone']
            islands = zone['islands']
        logistics_cost_out_cost = db.select_eu_express_expenses(supplier, express, country, billing_weight, region,
                                                                islands)
        if logistics_cost_out_cost:
            subtract = billing_weight * logistics_cost_out_cost['end_weight']
            delivery_cost = logistics_cost_out_cost['expenses'] + logistics_cost_out_cost[
                'additional_weight'] * subtract
        else:
            cost = 0.0
    elif "US" == country or "CA" == country:
        if "US" == country:
            logistics_cost_devliveries = db.select_us_delivery_cost(supplier, express, zone, billing_weight)
        else:
            logistics_cost_devliveries = db.select_ca_delivery_cost(supplier, express, zone, billing_weight)
        if logistics_cost_devliveries:
            if len(logistics_cost_devliveries) != 1:
                for logistics_cost in logistics_cost_devliveries:
                    if warehouse == logistics_cost['warehouse']:
                        delivery_cost = logistics_cost['expenses']
            else:
                delivery_cost = logistics_cost_devliveries[0]['expenses']
        if discount is not None:
            delivery_cost = delivery_cost * discount
    else:
        if ("FAL" == supplier and "GLS" == express) or ("金仓" == supplier and "GLS" == express):
            region = db.select_region(country, zip_code)
        logistics_cost_out_cost = db.select_eu_delivery_cost(supplier, express, country, billing_weight, region,
                                                             islands)
        if logistics_cost_out_cost:
            delivery_cost = logistics_cost_out_cost['expenses'] + logistics_cost_out_cost[
                'additional_weight'] * billing_weight
        else:
            cost = 0.0
        if discount is not None:
            delivery_cost = delivery_cost * discount

    return delivery_cost, cost


def get_house_cost(country, supplier, express, billing_weight, zip_code):
    """获得住宅附加费"""
    house_cost = 0.0
    if "GLS" == express and "Rush" == supplier:
        if db.select_rush_gls_zip(country, zip_code):
            house_cost = 13.1
    else:
        house_cost = db.select_house_cost(supplier, express, country, billing_weight)
    return house_cost


def get_custom_clearance_cost(country, supplier, express, zip_code):
    """获得清关费"""
    clearance_cost = 0.0
    clearance_costs = db.select_customs_clearance_fee(country, express, supplier)
    if clearance_costs:
        for item in clearance_costs:
            if item['str_zip'] is None or (item['str_zip'] <= zip_code <= item['end_zip']):
                clearance_cost = item['expenses']
    return clearance_cost


def other_additional_cost(country, supplier, express):
    """其他附加费"""
    select_country = "EU" if country != 'DE' else country
    other_cost_fsc = db.select_other_additional_cost(select_country, express, supplier)
    if other_cost_fsc:
        return other_cost_fsc[0]['expenses'], other_cost_fsc[0]['rate']
    return 0.0, 0.0


def get_out_bound_cost(country, supplier, express, goods):
    """出库费"""
    out_bound_cost = 0.0
    if "CTT" == express or "US" == country or "CA" == country:
        gross_weight_lb = goods['item_gross_weight']
        if "US" == country or "CA" == country:
            gross_weight_lb = math.ceil(goods['item_gross_weight'] * 2.20462)
        logistics_cost_out_cost = db.select_out_bound_cost(supplier, country, gross_weight_lb)
        if logistics_cost_out_cost:
            if logistics_cost_out_cost['base_weight']:
                gross = logistics_cost_out_cost['base_weight']
                out_bound_cost = logistics_cost_out_cost['expenses'] + gross * logistics_cost_out_cost[
                    'additional_weight']
            else:
                out_bound_cost = logistics_cost_out_cost['expenses']
    else:
        logistics_cost_out_cost = db.select_out_bound_cost(supplier, "DE", goods['item_gross_weight'])
        if logistics_cost_out_cost:
            if logistics_cost_out_cost['base_weight']:
                if "FAL" == supplier:
                    gross_base = round_up(goods['item_gross_weight'])
                    gross = round_up(gross_base - logistics_cost_out_cost['base_weight'])
                    out_bound_cost = logistics_cost_out_cost['expenses'] + gross * logistics_cost_out_cost[
                        'additional_weight']
                elif "金仓" == supplier or "Rush" == supplier:
                    gross_base = goods['item_gross_weight']
                    gross = round_up(gross_base - logistics_cost_out_cost['base_weight'])
                    out_bound_cost = logistics_cost_out_cost['expenses'] + gross * logistics_cost_out_cost[
                        'additional_weight']
                else:
                    out_bound_cost = (logistics_cost_out_cost['expenses'] +
                                      (goods['item_gross_weight'] - logistics_cost_out_cost['base_weight']) *
                                      logistics_cost_out_cost['additional_weight'])
    return out_bound_cost


def calculate_total_cost(goods_type_surcharge, special_surcharge, peak_season_surcharge, peak_season_type,
                         delivery_cost,
                         house_cost, bunker_surcharge_fsc, energy_surcharge_fsc, clearance_fee, other_cost,
                         other_surcharge_fsc, self_label_cost, out_bound_cost,
                         peak_season_surcharge_rate, country,
                         supplier, express, good_type, goods):
    """计算总物流成本"""
    goods_type_surcharge = round_up(goods_type_surcharge, 2)
    special_surcharge = round_up(special_surcharge, 2)
    peak_season_surcharge = round_up(peak_season_surcharge, 2)
    delivery_cost = round_up(delivery_cost, 2)
    house_cost = round_up(house_cost, 2)
    clearance_fee = round_up(clearance_fee, 2)
    other_cost = round_up(other_cost, 2)
    self_label_cost = round_up(self_label_cost, 2)
    out_bound_cost = round_up(out_bound_cost, 2)

    bunker_surcharge = 0.0
    energy_surcharge = 0.0

    total_cost = 0.0
    cost_item = {}

    total_cost += house_cost  #  住宅费
    cost_item['house_cost'] = house_cost

    total_cost += clearance_fee  #  清关费
    cost_item['clearance_fee'] = clearance_fee

    total_cost += goods_type_surcharge  # 货型附加费
    cost_item['goods_type_surcharge'] = goods_type_surcharge

    total_cost += other_cost  #  其他附加费
    cost_item['other_cost'] = other_cost

    total_cost += special_surcharge  # 特殊邮编附加费
    cost_item['special_surcharge'] = special_surcharge

    # 能源附加费
    if energy_surcharge_fsc is not None and energy_surcharge_fsc > 0.0 and "FAL" == supplier and "DPD" == express:
        energy_surcharge = round_up(total_cost * energy_surcharge_fsc, 2)
        total_cost += energy_surcharge
        cost_item['energy_surcharge'] = energy_surcharge
    else:
        cost_item['energy_surcharge'] = 0.0

    if other_surcharge_fsc is not None and other_surcharge_fsc > 0.0 and "CA" != country:
        other = total_cost * other_surcharge_fsc
        other_cost = round_up(other, 2)
        total_cost += other_cost  # 其他附加费按比例计算
        cost_item['other_cost'] = other_cost
    else:
        cost_item['other_cost'] = 0.0

    caadd = 0.0
    # 燃油附加费
    if bunker_surcharge_fsc is not None:
        if "US" == country or "CA" == country:
            add1 = total_cost + delivery_cost
            if peak_season_surcharge is not None:
                add1 += peak_season_surcharge
            bunker_surcharge = round_half(add1 * bunker_surcharge_fsc, 2)
            caadd = add1 + bunker_surcharge
        else:
            bunker_surcharge = round_half(total_cost * bunker_surcharge_fsc, 2)

    other1 = 0.0
    if other_surcharge_fsc and other_surcharge_fsc > 0 and "CA" == country and "FedEx" == express:
        other1 = round_half(caadd * other_surcharge_fsc, 2)
        other_cost = round_half(other1, 2)
        total_cost += other_cost
        cost_item['other_cost'] = other_cost
    else:
        cost_item['other_cost'] = 0.0

    total_cost += delivery_cost
    cost_item['delivery_cost'] = delivery_cost

    # 旺季附加费系数类型计算
    if peak_season_surcharge_rate:
        peek_season_surcharge = round_up(total_cost * peak_season_surcharge_rate, 2)
        total_cost += peek_season_surcharge
        cost_item['peak_season_surcharge'] = peak_season_surcharge

    total_cost += bunker_surcharge
    cost_item['bunker_surcharge'] = bunker_surcharge

    # 能源附加费
    if energy_surcharge_fsc and energy_surcharge_fsc >= 0.0 and (
            "西邮" == supplier or "中链" == supplier) and "DHL" == express:
        energy_surcharge = round_half(total_cost * energy_surcharge_fsc, 2)
        total_cost += energy_surcharge
        cost_item['energy_surcharge'] = energy_surcharge

    if peak_season_surcharge is not None:
        total_cost += peak_season_surcharge
        cost_item['peak_season_surcharge'] = peak_season_surcharge

    if "CA" == country and "文鼎" == supplier and "UPS" == express:
        energy_surcharge = round_half(total_cost * 0.15, 2)
        total_cost += energy_surcharge
        cost_item['energy_surcharge'] = energy_surcharge
        other_surcharge = round_half(total_cost * 0.1)
        total_cost += other_surcharge
        cost_item['other_cost'] = other_surcharge

    # 出库费
    if out_bound_cost:
        total_cost += out_bound_cost
        cost_item['out_bound_cost'] = out_bound_cost

    # 判断是否有超重附加费
    if "DPD特殊渠道" == express:
        if 31.5 < goods['item_gross_weight'] <= 40 and goods['item_gross_weight']:
            special_zip_surcharges_eu = db.select_eu_cost_type_surcharge(supplier, express, 'Overweight')
            if special_zip_surcharges_eu:
                item = special_zip_surcharges_eu[0]
                if good_type:
                    good_type = good_type + '+Overweight'
                else:
                    good_type = 'Overweight'
                goods_type_surcharge = item['expenses']
                total_cost += goods_type_surcharge
                cost_item['goods_type_surcharge'] = goods_type_surcharge
    cost_item['cost'] = round_half(total_cost, 2)
    return cost_item


def round_up(value: float, ndigits: int = 0) -> float:
    """
    向上取整到指定小数位，返回 float。
    ndigits >= 0: 保留小数位数
    ndigits < 0: 对 10,100,... 的位数向上取整
    例: round_up(1.234, 2) -> 1.24
         round_up(123.4, -1) -> 130.0
    """
    if value is None:
        raise ValueError("value 不能为空")
    v = float(value)
    if ndigits >= 0:
        factor = 10 ** ndigits
        return math.ceil(v * factor) / factor
    else:
        factor = 10 ** (-ndigits)
        return math.ceil(v / factor) * factor


def round_half(value: float, ndigits: int = 0) -> float:
    """
    四舍五入到指定小数位，返回 float。
    ndigits >= 0: 保留小数位
    ndigits < 0: 对 10,100,... 的位数四舍五入
    """
    if value is None:
        raise ValueError("value 不能为空")
    v = Decimal(str(value))
    # quant = 10 ** (-ndigits) in Decimal 表示要量化的步长，例如 ndigits=2 -> 0.01； ndigits=-1 -> 10
    quant = Decimal('1').scaleb(-ndigits)
    result = v.quantize(quant, rounding=ROUND_HALF_UP)
    return float(result)



def get_delivery_service(country, supplier, express, warehouse, good_type):
    """获取派送服务和服务代码，  好像不需要该函数"""
    country_name = db.select_eu_country_name(country)
    country_name = "EU" if country_name else country
    if "GLS" == express and "Rush" == supplier:
        country_name = "DE"
    if ("金仓德国仓-WT01" == warehouse or "金仓德国仓-KL01" == warehouse) and "GLS" == express:
        country_name = country
    if "金仓德国仓-S仓" == warehouse and "GLS" == express:
        country_name = country

    delivery_services = db.select_delivery_service(country_name, warehouse, express)

    if delivery_services:
        if len(delivery_services) == 1:
            return delivery_services[0]['delivery_service'], delivery_services[0]['service_code']
        else:
            for service in delivery_services:
                if good_type == service['goods_type']:
                    return service['delivery_service'], service['service_code']
    return None, None


if __name__ == '__main__':
    surcharge_list = estimate_logistics_cost(erp_sku='20FJGS-LOFT-J', country='CA', zip_code='V6M 2W2', state='BC')
    # surcharge_list = estimate_logistics_cost(erp_sku='80GDSJLEIAU20221219001B', country='US', zip_code='00522')
    for surcharge in surcharge_list:
        print(surcharge)
